#include "RandomEngine.h"
#include <iostream>
RandomEngineApp * g_pApp = NULL;

//---------------------------------------------------------------------------------------------------------------------
//Rangom Engine App Implementation
//---------------------------------------------------------------------------------------------------------------------
RandomEngineApp * const RandomEngineApp::Get()
{
	return g_pApp;
}
LogicPtr const RandomEngineApp::GetLogic()
{
	return m_logic;
}
IRenderer * const RandomEngineApp::GetRenderer()
{
	if(m_Renderer != RendererPtr())
	{
		return m_Renderer.get();
	}
	assert(0 && "Could not find active renderer.");
	return NULL;
}
IResCache* const RandomEngineApp::GetResCache()
{
	if(m_ResCache != ResCachePtr())
	{
		return m_ResCache.get();
	}
	assert(0 && "Could not find working resource cache.");
	return NULL;
}
IAssetManager * const RandomEngineApp::GetAssetManager()
{
	if(m_AssetManager != AssetManagerPtr())
	{
		return m_AssetManager.get();
	}
	assert(0 && "Could not find working asset manager.");
	return NULL;
}
RandomEngineApp::RandomEngineApp()
{
	g_pApp = this;
}
int RandomEngineApp::RunEngine(HINSTANCE hInstance, LPWSTR lpCmdLine, unsigned int screenWidth, unsigned int screenHeight, bool FullScreen, _TCHAR* window_name)
{
	m_bQuitting = false;
	ApplicationName = window_name;

#ifdef WIN32
	m_OSFramework = OSFrameworkPtr(RE_NEW WindowsFramework);
	m_OSFramework->VCreateMainWindow(screenWidth, screenHeight, window_name);
#endif
	if(m_OSFramework == OSFrameworkPtr())
	{
		assert(0 && "Could not find suitable version of OSFramework object. Please set manually the OS you are using!");
	}
	//set callbacks
	m_OSFramework->SetRandomEngineObject(this);
	m_OSFramework->SetUpdateCallback(&RandomEngineApp::OnUpdate);
	m_OSFramework->SetRenderCallback(&RandomEngineApp::OnRender);
	m_OSFramework->SetCreateDeviceCallback(&RandomEngineApp::OnCreateDevice);
	m_OSFramework->SetDestroyDeviceCallback(&RandomEngineApp::OnDestroyDevice);
	m_OSFramework->SetResetDeviceCallback(&RandomEngineApp::OnResetDevice);
	m_OSFramework->SetLostDeviceCallback(&RandomEngineApp::OnLostDevice);

	//THIS SHOUD BE READY EXACLY BEFORE CREATE DEVICE
	m_ResCache = ResCachePtr(RE_NEW ResCache(1024 * 1024 * 16));
	m_AssetManager = AssetManagerPtr(RE_NEW AssetManager); // first objects that are not beeing freed at runtime

	//It is time to create the rendering device
	m_Renderer = RendererPtr(m_OSFramework->VCreateRenderer(RER_D3D9));

	//Initialize RandomEngine's stuff
	if(ApplicationName.length() == 0) // set default header in case it is not setup
	{
		ApplicationName = L"Random Engine Default Header";
	}
	//Load Engine Resources
	LoadEngineResources(); // dynamic objects, freed and reconstructed when needed
	m_logic = CreateGameAndView();
	m_logic->VBuildInitialScene();


	//initialize the renderer effect TODO:MOVE THIS TO RENDERER
	REC()->VAddResourceFile(ResourceFilePtr(RE_NEW NoResourceFile("renderer.fx")));
	RE_Effect * ef = dynamic_cast<RE_Effect*>(REA()->GetAssetData(Resource("renderer.fx\\renderer.fx"))->back().get());
	g_pApp->GetRenderer()->VSetEffect(ef);

	m_OSFramework->VRun();
	m_OSFramework->VShutDown();
	return 0;
}
RandomEngineApp::~RandomEngineApp()
{
	ReleaseEngineResources(false); // release assets
	m_ResCache = ResCachePtr(); // release cache
	m_AssetManager = AssetManagerPtr(); // release asset manaer
	m_logic = LogicPtr(); // release logic;
	m_Renderer = RendererPtr(); // release renderer
	EventManager::Release();
	//strings last
	RE_StringTable::Get()->ReleaseStrings();
	g_pApp = NULL;
}


void RandomEngineApp::LoadEngineResources()
{
	m_AssetManager.get()->RestoreAssets();
}
void RandomEngineApp::ReleaseEngineResources(bool OnlyBuffers)
{
	m_AssetManager.get()->ReleaseAssets(OnlyBuffers);
}
/*
HRESULT CALLBACK RandomEngineApp::OnResetDevice( IDirect3DDevice9* pd3dDevice, const D3DSURFACE_DESC* pBackBufferSurfaceDesc, void *pUserContext  )
{
	if(g_pApp)
	{
		g_pApp->LoadEngineResources(); // load engine resources. It will load what is needed. Don't worry.
		g_pApp->GetRenderer()->VResetDeviceState();
		g_pApp->GetRenderer()->VSetDeclaration(VertexPosElements); // almost sure this can be moved in create device; It may be lost on redice reset. Be warned!
	}
	return S_OK;
}
void CALLBACK RandomEngineApp::OnLostDevice(void *pUserContext)
{
	g_pApp->ReleaseEngineResources(true); // release only dynamic objects. We are not terminating the whole process.
}
*/
bool RandomEngineApp::OnUpdate( float deltaTime )
{
	g_pApp->FrameTick(deltaTime);
	g_pApp->GetLogic()->VOnUpdate(deltaTime);
	return true;
}
bool RandomEngineApp::OnRender( float deltaTime )
{ 	
	RER()->VClear();
	RER()->VBeginScene();
	RER()->VClear();
	GetLogic()->VOnRender(); // ask the logic to pass the message to the human view

	RER()->VEndScene();
	return true;
}
void RandomEngineApp::OnLostDevice()
{
	ReleaseEngineResources(true);
}
void RandomEngineApp::OnResetDevice()
{
	g_pApp->LoadEngineResources(); // load engine resources. It will load what is needed. Don't worry.
	//g_pApp->GetRenderer()->VSetDeclaration(VertexPosElements);
}
void RandomEngineApp::OnCreateDevice()
{
}
void RandomEngineApp::OnDestroyDevice()
{
	//ReleaseEngineResources(true);
}
/*
HRESULT CALLBACK RandomEngineApp::OnCreateDevice( IDirect3DDevice9* pd3dDevice, const D3DSURFACE_DESC* pBackBufferSurfaceDesc, void* pUserContext ) { 


	return NULL;
};
void CALLBACK RandomEngineApp::OnDestroyDevice( void* pUserContext )
{
	//device is beeing destoyed. Free all resources so DXUT don't pop memory leak errors;
	g_pApp->ReleaseEngineResources(true);
}

LRESULT CALLBACK RandomEngineApp::MsgProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, bool* pbNoFurtherProcessing, void *pUserContext )
{
	switch (uMsg) 
	{
		case WM_CLOSE:
		{

			break;
		}
		case WM_KEYDOWN:
		{
			REE()->VTriggerEvent(EventDataPtr(RE_NEW EventData_Input_KeyDown(wParam)));
			break;
		}
		case WM_KEYUP:
		{
			REE()->VTriggerEvent(EventDataPtr(RE_NEW EventData_Input_KeyUp(wParam)));
			break;
		}
		case WM_MOUSEMOVE:
		{
			REE()->VTriggerEvent(EventDataPtr(RE_NEW EventData_Input_MouseMove(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))));
			break;
		}
		case WM_LBUTTONDOWN:
		{
			REE()->VTriggerEvent(EventDataPtr(RE_NEW EventData_Input_KeyDown(char(254))));
			break;
		}
		case WM_LBUTTONUP:
		{
			REE()->VTriggerEvent(EventDataPtr(RE_NEW EventData_Input_KeyUp(char(254))));
			break;
		}
		case WM_RBUTTONDOWN:
		{
			REE()->VTriggerEvent(EventDataPtr(RE_NEW EventData_Input_KeyDown(char(255))));
			break;
		}
		case WM_RBUTTONUP:
		{
			REE()->VTriggerEvent(EventDataPtr(RE_NEW EventData_Input_KeyUp(char(255))));
			break;
		}
	}
	return S_OK;
}
*/
const TCHAR* RandomEngineApp::VGetName() 
{ 
	return ApplicationName.c_str();
};
const std::string & RandomEngineApp::GetLogDirectory()
{
	return LogDirectory;
}
void RandomEngineApp::SetLogDirectory(const std::string & dir)
{
	LogDirectory = dir;
}
const double RandomEngineApp::GetTimeElapsed()
{
	return m_ElapsedTime;
}
const float RandomEngineApp::GetFrameTime()
{
	return m_FrameTime;
}
void RandomEngineApp::FrameTick(const float frame_time)
{
	m_FrameTime = frame_time;
	m_ElapsedTime += frame_time;
}
//---------------------------------------------------------------------------------------------------------------------
//Base Game Logic Implementation
//---------------------------------------------------------------------------------------------------------------------
void BaseGameLogic::VHandleEvent(EventDataPtr EventData)
{
	float fTime = RE()->GetFrameTime(); // it would be way better if the values below are beeing saved somewhere and used in OnUpdate method
	if(EventData->VGetEventType() == EventData_View_MoveForward::m_EventType)
	{
		EventData_View_MoveForward * ptr = EventData->GetData<EventData_View_MoveForward>();
		VMoveActor(ptr->m_actor, ptr->m_dir * fTime * Vec3(0,0,1));
	}else if(EventData->VGetEventType() == EventData_View_MoveLeft::m_EventType)
	{
		EventData_View_MoveLeft * ptr = EventData->GetData<EventData_View_MoveLeft>();
		VMoveActor(ptr->m_actor, ptr->m_dir * fTime * Vec3(-1,0,0));
	}
}

void BaseGameLogic::VAddView(GameViewPtr view)
{
	m_GameViewList.push_back(view);
}
void BaseGameLogic::VRemoveView(GameViewPtr view)
{
	GameViewList::iterator begin = m_GameViewList.begin();
	GameViewList::iterator end = m_GameViewList.end();
	while(begin != end)
	{
		if(&*(*begin).get() == &*view.get())
		{
			m_GameViewList.remove(view);
		}
		begin++;
	}
}
ActorId BaseGameLogic::VGenerateActorId()
{
	return ++m_currentid;
}
void BaseGameLogic::VAddActor(ActorPtr actor)
{
	m_ActorMap[actor->VGetId()] = actor;
}
void BaseGameLogic::VRemoveActor(const ActorId id)
{

}
void BaseGameLogic::VMoveActor(const ActorId id, const Vec3 & dir) // fast translate function
{
	VGetActor(id)->GetComponent<TransformComponent>(TransformComponent::m_ComponentType)->Translate(dir);
}
ActorPtr BaseGameLogic::VGetActor(const ActorId id)
{
	ActorMap::iterator actormap_search = m_ActorMap.find(id);
	if(actormap_search == m_ActorMap.end())
	{
		assert(0 && "Actor not found.");
	}
	return actormap_search->second;
}
///////////////////////////////////////////////
////Build Initial Scene()
////Builds Random Engine's test scene;
////WARNING!!! SHOUD NOT BE CALLED IN REAL GAME
////
////Good Idea to overwrite and put all game init
//// here
///////////////////////////////////////////////
bool BaseGameLogic::VBuildInitialScene()
{
	REC()->VAddResourceFile(ResourceFilePtr(RE_NEW NoResourceFile("level.rel")));
	AssetDataList * list = REA()->GetAssetData(Resource("level.rel\\level.rel"));
	VLoadScene(list->front());
	return true;
}


BaseGameLogic::BaseGameLogic()
{
	m_currentid = 0;
	m_Listener = EventListenerPtr(RE_NEW BaseLogicListener(this));
	//now listen for View type events
	REE()->VAddListener(m_Listener, EventData_View_MoveForward::m_EventType);
	REE()->VAddListener(m_Listener, EventData_View_MoveLeft::m_EventType);
	REE()->VAddListener(m_Listener, EventData_View_Rotate::m_EventType);
}
BaseGameLogic::~BaseGameLogic()
{
	REE()->VRemoveListener(m_Listener, EventData_View_MoveForward::m_EventType);
	REE()->VRemoveListener(m_Listener, EventData_View_MoveLeft::m_EventType);
	REE()->VRemoveListener(m_Listener, EventData_View_Rotate::m_EventType);
}
void BaseGameLogic::VOnRender()
{
	GameViewList::iterator begin = m_GameViewList.begin();
	GameViewList::iterator end = m_GameViewList.end();
	while(begin != end)
	{
		(*begin)->VOnRender();
		begin++;
	}
}
void BaseGameLogic::VOnUpdate(float fTime)
{
	GameViewList::iterator begin = m_GameViewList.begin();
	GameViewList::iterator end = m_GameViewList.end();
	while(begin != end)
	{
		(*begin)->VOnUpdate(fTime);
		begin++;
	}
}

bool BaseGameLogic::VLoadScene(AssetDataPtr level)
{
	AssetDataList actors = level->GetChildrenByType(ActorData::m_ActorDataType);
	AssetDataList::iterator i = actors.begin();
	while(i != actors.end())
	{
		VAddActor(dynamic_cast<ActorData*>((*i).get())->CreateActor());
		++i;
	}
	return true;
}
bool BaseGameLogic::VClearScene()
{
	return true;
}

void BaseLogicListener::HandleEvent(EventDataPtr EventData)
{
	m_Logic->VHandleEvent(EventData);
}